package com.zhanghe.study.mybatis.interceptors;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.sql.Connection;
import java.util.Map;
import java.util.Properties;

/**
 * @author zh
 * @date 2021/4/13 15:11
 */
// @Signature其实就是一个需要拦截的方法封装
// type表示拦截那个类，就是mybatis中的四个核心接口Executor、ParameterHandler、ResultSetHandler 以及 StatementHandler
// method表示拦截哪个方法，args为该方法所需参数

    // StatementHandler#prepare是用来从Connection获取Statement对象的
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class,Integer.class})})
public class MyPageInterceptor implements Interceptor {

    private int page;
    private int size;
    @SuppressWarnings("unused")
    private String dbType;

    @SuppressWarnings("unchecked")
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 获取需要加载的对象，即所拦截的类对象
        StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
        // mybatis中的反射方法
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        // 分离代理对象链(由于目标类可能被多个拦截器拦截，从而形成多次代理，通过循环来进行分离出最原始的目标类)
        while(metaObject.hasGetter("h")){
            Object object = metaObject.getValue("h");
            metaObject = SystemMetaObject.forObject(object);
        }
        // 分离最后一个代理对象的目标类
        while(metaObject.hasGetter("target")){
            Object object = metaObject.getValue("target");
            metaObject = SystemMetaObject.forObject(object);
        }
        // 需要属性mappedStatement来获取配置文件中的内容
        MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
        String mapId = mappedStatement.getId();
        if(mapId.matches(".+ByPager$")){
            ParameterHandler parameterHandler = (ParameterHandler)metaObject.getValue("delegate.parameterHandler");
            Map<String, Object> params = (Map<String, Object>)parameterHandler.getParameterObject();
            page = (int)params.get("page");
            size = (int)params.get("size");
            // 取出即将执行的sql
            String sql = (String) metaObject.getValue("delegate.boundSql.sql");
            sql += " limit "+(page-1)*size +","+size;
            metaObject.setValue("delegate.boundSql.sql", sql);
        }
        return invocation.proceed();
    }

    // target是拦截器要拦截的对象
    @Override
    public Object plugin(Object target) {
        // 使用JDK的动态代理，给target对象创建一个delegate代理对象，以此来实现方法拦截和增强功能，它会回调intercept()方法
        return Plugin.wrap(target, this);
    }

    // 用来传递插件的参数
    @Override
    public void setProperties(Properties properties) {
        String limit = properties.getProperty("limit","10");
        this.page = Integer.parseInt(limit);
        this.dbType = properties.getProperty("dbType", "mysql");
    }

}
